#ifndef BLOCKQUEUE_H
#define BLOCKQUEUE_H

#include <cassert>
#include <condition_variable>
#include <mutex>
#include <vector>
#include <deque>

template <class T>
class BlockDeque {
public:
    explicit BlockDeque(std::size_t MaxCapacity = 1000);
    ~BlockDeque();

    void clear();
    bool empty();
    bool full();
    void Close();

    std::size_t size();
    std::size_t capacity();
    
    T front();
    T back();

    void push_back(const T& item);
    void push_front(const T& itme);
    bool pop(T& item);
    bool pop(T& item, int timeout);
    void flush();

private:
    std::deque<T> deq_;
    // 规模
    std::size_t capacity_;
    std::mutex mtx_;

    // 队列状态
    bool isClose_;
    
    // 生产者消费者模型还不是很理解
    std::condition_variable condConsumer_;
    std::condition_variable condProducer_;
};

using size_t = std::size_t;

template<class T>
BlockDeque<T>::BlockDeque(size_t MaxCapacity) :capacity_(MaxCapacity) {
    assert(MaxCapacity > 0);
    isClose_ = false;
}

template<class  T>
BlockDeque<T>::~BlockDeque() {
    Close();
}

template<class T>
void BlockDeque<T>::Close() {
    {
        std::lock_guard<std::mutex> locker(mtx_);
        deq_.clear();
        isClose_ = true;
    }
    // 告知与这两个条件变量相关的线程，队列的状态已经发生改变
    condProducer_.notify_all();
    condConsumer_.notify_all();
}

template<class T>
void BlockDeque<T>::flush() {
    // 通知消费者，表示有任务可以做
    condConsumer_.notify_one();
}

template<class T>
void BlockDeque<T>::clear() {
    std::lock_guard<std::mutex> locker(mtx_);
    deq_.clear();
}

template<class T>
T BlockDeque<T>::front() {
    std::lock_guard<std::mutex> locker(mtx_);
    return deq_.front();
}

template<class T>
T BlockDeque<T>::back() {
    std::lock_guard<std::mutex> locker(mtx_);
    return deq_.back();
}

template<class T>
size_t BlockDeque<T>::size() {
    std::lock_guard<std::mutex> locker(mtx_);
    return deq_.size();
}

template<class T>
size_t BlockDeque<T>::capacity() {
    std::lock_guard<std::mutex> locker(mtx_);
    return capacity_;
}

template<class T>
void BlockDeque<T>::push_back(const T &item) {
    std::unique_lock<std::mutex> locker(mtx_);
    // 队列满了的时候，生产者阻塞
    while(deq_.size() >= capacity_) {
        condProducer_.wait(locker);
    }
    /*
        队列不是满的
        就将任务加入队列中
        然后通知消费者来消费
    */
    deq_.push_back(item);
    condConsumer_.notify_one();
}

template<class T>
void BlockDeque<T>::push_front(const T& item) {
    std::unique_lock<std::mutex> locker(mtx_);
    while(deq_.size() >= capacity_) {
        condProducer_.wait(locker);
    }
    deq_.push_front(item);
    condProducer_.notify_one();
}

template<class T>
bool BlockDeque<T>::empty() {
    std::lock_guard<std::mutex> locker(mtx_);
    return deq_.empty();
}

template<class T>
bool BlockDeque<T>::full() {
    std::lock_guard<std::mutex> locker(mtx_);
    return deq_.size() >= capacity_;
}

// 弹出最后一个的同时，要获取该元素
template<class T>
bool BlockDeque<T>::pop(T& item) {
    std::unique_lock<std::mutex> locker(mtx_);
    while(deq_.empty()) {
        // 等待消费者被通知
        condConsumer_.wait(locker);
        if(isClose_) {
            return false;
        }
    }
    item = deq_.front();
    deq_.pop_front();
    // 有一个位置了，通知生产者可以生产
    condProducer_.notify_one();
    return true;
}

template<class T>
bool BlockDeque<T>::pop(T& item, int timeout) {
    std::unique_lock<std::mutex> locker(mtx_);
    while(deq_.empty()) {
        if(condConsumer_.wait_for(locker, std::chrono::seconds(timeout))
        == std::cv_status::timeout) {
            return false;
        }
        if(isClose_) {
            return false;
        }
    }
    item = deq_.front();
    deq_.pop_front();
    condProducer_.notify_one();
    return true;
}

#endif // BLOCKQUEUE_H